#include "LogicSystem.h"
#include "LogicNode.h"
#include "const.h"
#include "CSession.h"
#include <iostream>
#include "StatusGrpcClient.h"
#include "const.h"
#include "MysqlMgr.h"
#include "RedisMgr.h"
#include "ConfigMgr.h"
#include "UserMgr.h"
#include "message.pb.h"
#include "ChatGrpcClient.h"

LogicSystem::LogicSystem()
:_stop_recv_msg_thread(false)
{
    //注册回调函数
    RegisterFunCallBack();
    
    //创建一个线程，用于处理逻辑节点
    _recv_msg_thread=std::thread(&LogicSystem::DealLogicNode,this);
}

void LogicSystem::DealLogicNode()
{
    while(!_stop_recv_msg_thread)
    {
        std::unique_lock<std::mutex> lock(_recv_msg_queue_mutex);
        _recv_msg_queue_cv.wait(lock,[this](){
            return !_recv_msg_queue.empty()||_stop_recv_msg_thread;
        });

        if(_stop_recv_msg_thread)
        {
            while(!_recv_msg_queue.empty())
            {
                std::shared_ptr<LogicNode>  logic_node = _recv_msg_queue.front();
                _recv_msg_queue.pop();
                lock.unlock();
                
                MSG_IDS msgID = (MSG_IDS)logic_node->_recv_msg_node->_msg_id;

                if(_fun_callbacks.find(msgID)!=_fun_callbacks.end())
                {
                    _fun_callbacks[msgID](logic_node->_session,msgID,
                                           std::string(logic_node->_recv_msg_node->_data,
                                                    logic_node->_recv_msg_node->_total_len));
                }else
                {
                    std::cout<<"DealLogicNode msg_id:"<<msgID<<" not found"<<std::endl;
                }
                lock.lock();
            }

            return;
        }
        std::shared_ptr<LogicNode> logic_node=_recv_msg_queue.front();
        _recv_msg_queue.pop();
        lock.unlock();
        MSG_IDS msgID = (MSG_IDS)logic_node->_recv_msg_node->_msg_id;
        //从_fun_callbacks中查找对应的函数，执行回调
        auto it = _fun_callbacks.find(msgID);
        if(it!=_fun_callbacks.end())
        {
            it->second(logic_node->_session,msgID,
                std::string(logic_node->_recv_msg_node->_data,
                        logic_node->_recv_msg_node->_total_len));
            continue;
        }
        //如果没有找到对应的函数，打印日志
        std::cout<<"DealLogicNode msg_id:"<<msgID<<" not found"<<std::endl;

    }
}

void LogicSystem::PostLogicNodeToQueue(std::shared_ptr<LogicNode> logic_node)
{
    std::unique_lock<std::mutex> lock(_recv_msg_queue_mutex);
    if(_stop_recv_msg_thread)
    {
        return;
    }
    _recv_msg_queue.push(logic_node);
    _recv_msg_queue_cv.notify_one();
}


LogicSystem::~LogicSystem()
{
    Close();
}

void LogicSystem::Close()
{
    {

        std::lock_guard<std::mutex> lock(_recv_msg_queue_mutex);
        _stop_recv_msg_thread=true;
        _recv_msg_queue_cv.notify_all();
    }

    _recv_msg_thread.join();
}

void LogicSystem::RegisterFunCallBack()
{
    _fun_callbacks[MSG_IDS::MSG_CHAT_LOGIN]=std::bind(&LogicSystem::LoginHandler,this,
                                                                    std::placeholders::_1,
                                                                        std::placeholders::_2,std::placeholders::_3);

    _fun_callbacks[MSG_IDS::ID_SEARCH_USER_REQ]=std::bind(&LogicSystem::SearchUserHandler,this,
                                                                    std::placeholders::_1,
                                                                        std::placeholders::_2,std::placeholders::_3);
    _fun_callbacks[MSG_IDS::ID_ADD_FRIEND_REQ]=std::bind(&LogicSystem::AddFriendApplyHandler,this,
                                                                    std::placeholders::_1,
                                                                        std::placeholders::_2,std::placeholders::_3);                                                                   
}

void LogicSystem::LoginHandler(std::shared_ptr<CSession> session,const short& msg_id,const std::string &msg_data)
{
    Json::Reader reader;
    Json::Value root;
    Json::Value rtvalue;

    bool b_parse = reader.parse(msg_data,root);
    if (!b_parse)
    {
        std::cout<<"LoginHandler parse msg_data failed"<<std::endl;
        return;
    }
    int uid = root["uid"].asInt();
    std::cout<<"LoginHandler uid = "<<uid<< \
    " token = "<<root["token"].asString()<<std::endl;

    Defer defer([this,session,&rtvalue](){
        std::string jsonStr = rtvalue.toStyledString();
        //Session将消息异步的发送出去
        session->Send(jsonStr,MSG_IDS::MSG_CHAT_LOGIN_RSP);
    });




    // auto rsp = StatusGrpcClient::GetInstance()->Login(root["uid"].asInt(),
    //                                                 root["token"].asString());
    //从redis中查询token
    std::string uid_str = std::to_string(uid);
    std::string token="";
    bool success = RedisMgr::GetInstance()->Get(USERTOKENPREFIX+uid_str,token);
    if (!success)
    {
        rtvalue["error"] = ErrorCodes::TokenInvalid;
        return;
    }
    //判断token是否匹配
    if(token!=root["token"].asString())
    {
        rtvalue["error"] = ErrorCodes::TokenInvalid;
        return;
    }
    
    
    //调用GetBaseInfo方法，此方法内部会到Redis中查找用户信息，如果用户不存在，到数据库中去查数据
    std::shared_ptr<UserInfo> user_info=std::make_shared<UserInfo>();
    std::string base_key = USER_BASE_INFO+uid_str;
    success = GetBaseInfo(base_key,uid,user_info);
    if (!success)
    {
        rtvalue["error"] = ErrorCodes::UidInvalid;
        return;
    }
    
    rtvalue["error"] = ErrorCodes::Success;
    rtvalue["uid"] = uid;
    rtvalue["pwd"] = user_info->pwd;
    rtvalue["name"] = user_info->name;
    rtvalue["email"] = user_info->email;
    rtvalue["nick"] = user_info->nick;
    rtvalue["desc"] = user_info->desc;
    rtvalue["sex"] = user_info->sex;
    rtvalue["icon"] = user_info->icon;
    


    // 从数据库获取申请列表
    std::vector<std::shared_ptr<ApplyInfo>> apply_list;
    bool ret = GetFriendApplyInfo(uid,apply_list);
    if(ret)
    {
        for (auto &apply : apply_list)
        {
            Json::Value obj;
            obj["name"] = apply->_name;
            obj["uid"] = apply->_uid;
            obj["icon"] = apply->_icon;
            obj["sex"] = apply->_sex;
            obj["desc"] = apply->_desc;
            obj["status"] = apply->_status;
            obj["nick"] = apply->_nick;
            // 在apply_list中添加obj
            rtvalue["apply_list"].append(obj);
        }
        
    }

    // 从数据库获取好友列表


    // 检查本服务器在redis中的连接数 然后增加1
    std::string server_name = ConfigMgr::GetInstance()["SelfServer"]["Name"];
    std::string conn_count = RedisMgr::GetInstance()->HGet(LOGIN_COUNT,server_name);
    int count = 0;
    if(!conn_count.empty())
    {
        count = std::stoi(conn_count);
    }
    count++;
    //将count写入redis
    RedisMgr::GetInstance()->HSet(LOGIN_COUNT,server_name,std::to_string(count));

    //session绑定用户的uid
    session->SetUserId(root["uid"].asInt());

    //将用户对应的ip写入到redis中
    RedisMgr::GetInstance()->Set(USERIPPREFIX+uid_str,server_name);

    //将用户的session绑定到UserMgr中
    UserMgr::GetInstance()->SetUserSession(root["uid"].asInt(),session);

    return;
}


bool LogicSystem::GetBaseInfo(const std::string & base_key,int uid,std::shared_ptr<UserInfo>& user_info)
{
    //首先从redis中查找用户信息
    std::string info_str="";
    bool b_base = RedisMgr::GetInstance()->Get(base_key,info_str);
    if(b_base)
    {
        //解析info_str
        Json::Reader reader;
        Json::Value root;
        bool b_parse = reader.parse(info_str,root);
        if (!b_parse)
        {
            std::cout<<"GetBaseInfo parse info_str failed"<<std::endl;
            return false;
        }
        //从root中提取信息放到user_info中
        user_info = std::make_shared<UserInfo>();
        user_info->uid = uid;
        user_info->name = root["name"].asString();
        user_info->pwd = root["pwd"].asString();
        user_info->email = root["email"].asString();
        user_info->nick = root["nick"].asString();
        user_info->desc = root["desc"].asString();
        user_info->sex = root["sex"].asInt();
        user_info->icon = root["icon"].asString();


        return true;
    }
    else
    {
        std::shared_ptr<UserInfo> user_info_from_db=nullptr;
        //如果redis中没有数据，则需要到数据库中去查询
        user_info_from_db = MysqlMgr::GetInstance()->GetUser(uid);
        if(user_info_from_db==nullptr)
        {
            return false;
        }
        user_info = user_info_from_db;

        //将用户信息放入redis中
        Json::Value root;
        root["uid"] = user_info->uid;
        root["name"] = user_info->name;
        root["pwd"] = user_info->pwd;
        root["email"] = user_info->email;
        root["nick"] = user_info->nick;
        root["desc"] = user_info->desc;
        root["sex"] = user_info->sex;
        root["icon"] = user_info->icon;

        std::string user_str = root.toStyledString();
        RedisMgr::GetInstance()->Set(base_key,user_str);
        return true;
    }
}


void LogicSystem::SearchUserHandler(std::shared_ptr<CSession> session,const short& msg_id,const std::string &msg_data)
{
    // 解析字符串为json
    Json::Reader reader;
    Json::Value root;
    Json::Value value;
    // 使用Defer类在函数退出时自动发送恢复
    Defer defer([&value,session](){
        session->Send(value.toStyledString(),MSG_IDS::ID_SEARCH_USER_RSP);
    });

    bool b_parse = reader.parse(msg_data,root);
    if (!b_parse)
    {
        root["error"] = ErrorCodes::Error_Json;
        return;
    }
    std::string uid_str = root["uid"].asString();
    std::cout<<"SearchUserHandler uid:"<<uid_str<<std::endl;

    // 判断uid到底是数字还是字符串
    if(isPureDigit(uid_str))
    {
        // uid是数字

        GetUserByUid(uid_str,value);
        
    }
    else
    {
        // uid是字符串
        GetUserByName(uid_str,value);
    }
}


bool LogicSystem::isPureDigit(const std::string &str)
{
    // 使用范围for循环依次遍历每一个字符判断是否为数字，只要有一个不是就返回false
    for(char c:str)
    {
        if(!std::isdigit(c))
        {
            return false;
        }
    }
    return true;

}

void LogicSystem::GetUserByUid(const std::string& uid_str,Json::Value& value)
{
    value["error"]=ErrorCodes::Success;
    // TODO 先到redis中查看看有没有，没有再到mysql中查询
    std::string userinfo = "";
    bool flag = RedisMgr::GetInstance()->Get(USER_BASE_INFO+uid_str,userinfo);
    //如果查到了用户信息
    if(flag)
    {
        Json::Reader reader;
        Json::Value root;
        bool b_parse = reader.parse(userinfo,root);
        if (!b_parse)
        {
            std::cout<<"GetUserByUid parse userinfo failed"<<std::endl;
            value["error"]=ErrorCodes::Error_Json;
            return;
        }
        //输出用户信息
        std::cout<<"GetUserByUid uid:"<<root["uid"].asInt() \
        <<" name:"<<root["name"].asString() \
        <<" pwd:"<<root["pwd"].asString() \
        <<" email:"<<root["email"].asString() \
        <<" nick:"<<root["nick"].asString() \
        <<" desc:"<<root["desc"].asString() \
        <<" sex:"<<root["sex"].asInt() \
        <<" icon:"<<root["icon"].asString() \
        <<std::endl;

        value["error"]=ErrorCodes::Success;
        value["uid"]=root["uid"].asInt();
        value["name"]=root["name"].asString();
        value["pwd"]=root["pwd"].asString();
        value["email"]=root["email"].asString();
        value["nick"]=root["nick"].asString();
        value["desc"]=root["desc"].asString();
        value["sex"]=root["sex"].asInt();
        value["icon"]=root["icon"].asString();
        return;
    }
    // 没查到就去数据库中查询
    int uid = std::stoi(uid_str);
    std::shared_ptr<UserInfo> userInfo = nullptr;
    // 到数据库中去查找用户信息
    userInfo = MysqlMgr::GetInstance()->GetUser(uid);
    if (!userInfo)
    {
        value["error"]=ErrorCodes::Error_Json;
        return;
    }
    // 将数据放入json中
    value["error"]=ErrorCodes::Success;
    value["uid"]=userInfo->uid;
    value["name"]=userInfo->name;
    value["pwd"]=userInfo->pwd;
    value["email"]=userInfo->email;
    value["nick"]=userInfo->nick;
    value["desc"]=userInfo->desc;
    value["sex"]=userInfo->sex;
    value["icon"]=userInfo->icon;

    // 输出查询到的用户信息
    std::cout<<"GetUserByUid uid:"<<userInfo->uid \
    <<" name:"<<userInfo->name \
    <<" pwd:"<<userInfo->pwd \
    <<" email:"<<userInfo->email \
    <<" nick:"<<userInfo->nick \
    <<" desc:"<<userInfo->desc \
    <<" sex:"<<userInfo->sex \
    <<" icon:"<<userInfo->icon \
    <<std::endl;
    // 将数据先存到redis中
    RedisMgr::GetInstance()->Set(USER_BASE_INFO+uid_str,value.toStyledString());

    return;

}
void LogicSystem::GetUserByName(const std::string& name,Json::Value& value)
{
    value["error"]=ErrorCodes::Success;
    // 直接到mysql中去查询用户信息
    std::shared_ptr<UserInfo> userInfo = nullptr;
    // 到数据库中去查找用户信息
    userInfo = MysqlMgr::GetInstance()->GetUserByName(name);
    if (!userInfo)
    {
        value["error"]=ErrorCodes::Error_Json;
        return;
    }
    // 将数据放入json中
    value["error"]=ErrorCodes::Success;
    value["uid"]=userInfo->uid;
    value["name"]=userInfo->name;
    value["pwd"]=userInfo->pwd;
    value["email"]=userInfo->email;
    value["nick"]=userInfo->nick;
    value["desc"]=userInfo->desc;
    value["sex"]=userInfo->sex;
    value["icon"]=userInfo->icon;

    //将数据先存到redis中
    RedisMgr::GetInstance()->Set(USER_BASE_INFO+name,value.toStyledString());

}


void LogicSystem::AddFriendApplyHandler(std::shared_ptr<CSession> session,const short& msg_id,const std::string &msg_data)
{
    // json字符串
    Json::Reader reader;
    Json::Value root;
    Json::Value rtvalue;
    rtvalue["error"]=ErrorCodes::Success;

    bool b_parse = reader.parse(msg_data,root);
    if (!b_parse)
    {
        std::cout<<"AddFriendApplyHandler parse msg_data failed"<<std::endl;
        rtvalue["error"]=ErrorCodes::Error_Json;
        return;
    }
    // 利用RAII思想，在函数结束时自动将消息发送给客户端
    Defer defer([session,&rtvalue](){
        
        session->Send(rtvalue.toStyledString(),MSG_IDS::ID_ADD_FRIEND_RSP);
    });
    // 从json中提取数据
    
    int uid = root["uid"].asInt();
    std::string applyname = root["applyname"].asString();
    std::string backname = root["backname"].asString();
    int to_uid = root["touid"].asInt();

    // 输出提取到的数据
    std::cout<<"AddFriendApplyHandler uid:"<<uid \
    <<" applyname:"<<applyname \
    <<" backname:"<<backname \
    <<" touid:"<<to_uid \
    <<std::endl;

    // 将申请信息写入数据库，然后到redis中查询用户是否在线，不在直接return，在线的话，查看是否在同一服务器，在直接找出对方的session发送好友申请，不在使用gRPC通知用户有新的好友申请
    // 将申请信息写入数据库
    MysqlMgr::GetInstance()->AddFriendApply(uid,to_uid);

    // 到redis中查询用户是否在线
    std::string toUserChatServerName ="";
    bool b_ip = RedisMgr::GetInstance()->Get(USERIPPREFIX+std::to_string(to_uid),toUserChatServerName);
    if (!b_ip)
    {
        std::cout<<"AddFriendApplyHandler to_uid:"<<to_uid<<" not online"<<std::endl;
        return;
    }

    // 查看是否在同一服务器
    auto& configMgr = ConfigMgr::GetInstance();
    std::string selfServerName = configMgr["SelfServer"]["Name"];
    if (selfServerName != toUserChatServerName)
    {
        // 不在同一服务器，使用gRPC通知用户有新的好友申请
        std::cout<<"AddFriendApplyHandler to_uid:"<<to_uid<<" not in same server"<<std::endl;
        // 获取对方服务器的IP地址
        std::string basekey = USER_BASE_INFO+ std::to_string(to_uid);
        std::shared_ptr<UserInfo> userInfo = nullptr;
        bool ret = GetBaseInfo(basekey,uid,userInfo);
        if (!ret)
        {
            std::cout<<"AddFriendApplyHandler to_uid:"<<to_uid<<" not found baseinfo"<<std::endl;
            return;
        }
        /*
            int32 applyuid = 1;
            //申请添加好友的用户的昵称
            string name=2;
            //申请添加好友的用户描述
            string desc=3;
            //目标添加好友的用户id
            string touid=4;
        */
        ::message::AddFriendReq request;
        request.set_applyuid(uid);
        request.set_name(applyname);
        request.set_desc("");
        request.set_touid(to_uid);
        request.set_icon(userInfo->icon);
        request.set_nick(userInfo->nick);
        request.set_sex(userInfo->sex);
        ::message::AddFriendRsp rsp = ChatGrpcClient::GetInstance()->NotifyAddFriend(toUserChatServerName,request);
        if (rsp.error()!=ErrorCodes::Success)
        {
            std::cout<<"AddFriendApplyHandler to_uid:"<<to_uid<<" NotifyAddFriend failed"<<std::endl;
        }
        
        return;
    }
    // 在同一服务器，直接找出对方的session发送好友申请
    std::shared_ptr<CSession> toSession = UserMgr::GetInstance()->GetSession(to_uid);
    if (!toSession)
    {
        std::cout<<"AddFriendApplyHandler to_uid:"<<to_uid<<" not found session"<<std::endl;
        return;
    }
    // 发送好友申请
    Json::Value applyvalue;
    applyvalue["error"]=ErrorCodes::Success;
    applyvalue["applyuid"]=uid;
    applyvalue["name"]=applyname;
    applyvalue["desc"]="";
    toSession->Send(applyvalue.toStyledString(),MSG_IDS::ID_NOTIFY_ADD_FRIEND_REQ);

}

bool LogicSystem::GetFriendApplyInfo(int to_uid,std::vector<std::shared_ptr<ApplyInfo>>& apply_list)
{
    // 从数据库中获取好友申请列表
    return MysqlMgr::GetInstance()->GetApplyFriendList(to_uid,apply_list,0,10);

}
